
import { AxiosRequestConfig, AxiosPromise, AxiosResponse } from '../types/index';
import { parseHeaders } from '../helpers/headers';
import { createError } from '../helpers/error'
import { isURLSameOrigin, isFormData } from '../helpers/util'
import cookie from '../helpers/cookie'

/** 原生ajax发送请求 */
export default function xhr(config: AxiosRequestConfig): AxiosPromise {
  return new Promise((resolve, reject ) => {
      const { data = null, url, method = 'get', headers = {}, 
              responseType, timeout, cancelToken, withCredentials, 
              xsrfCookieName, xsrfHeaderName, onDownloadProgress, onUploadProgress,
              auth, validateStatus } = config;
              
      const request = new XMLHttpRequest();
      request.open(method.toLocaleUpperCase(), url!, true);
     
      configureRequest()

      addEvents()
  
      processHeaders()
  
      processCancel()
     
      request.send(data)

      function configureRequest(): void {
          // 请求响应类型
          if ( responseType ) {
            request.responseType =  responseType
          }

          //超时时间设置
          if( timeout ) {
            request.timeout = timeout
          }

          // https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest/withCredentials
          // 跨域请求： 设置withCredentials 为true获得的第三方cookies
          if(withCredentials) {
            request.withCredentials = withCredentials
          }
      }

      function addEvents(): void {
        request.onreadystatechange = function() {
          if(request.readyState !== 4) {
            return
          }
          // 当出现网络错误或者超时错误的时候，status都为 0
          if(request.status === 0) {
            return
          }
  
          // header字符串转JSON对象
          const responseHeaders = parseHeaders(request.getAllResponseHeaders());
          const responseData = responseType && responseType !== 'text' ? request.response : request.responseText;
          const response: AxiosResponse = {
            data: responseData,
            status: request.status,
            statusText: request.statusText,
            headers: responseHeaders,
            config,
            request
          }
          
          // resolve(response);
          handerResponse(response)
        }

          // 网络请求等错误
          request.onerror = function() {
            reject(createError('Network Error', config, '0', request))
          }

          // 超时
          request.ontimeout = function() {
            reject(createError(`Timeout of ${timeout} ms exceeded`, config, 'ECONNABORTED',request))
          }
          
          // 进度
          if (onDownloadProgress) {
            request.onprogress = onDownloadProgress
          }
    
          if (onUploadProgress) {
            request.upload.onprogress = onUploadProgress
          }
        
      }
      
      function processHeaders(): void {
        // 请求的数据是 FormData 类型，我们应该主动删除请求 headers 中的 Content-Type 字段，让浏览器自动根据请求数据设置 Content-Type
        if (isFormData(data)) {
          delete headers['Content-Type']
        }

        // XSRS
        if ((withCredentials || isURLSameOrigin(url!)) && xsrfCookieName){
          const xsrfValue = cookie.read(xsrfCookieName)
          if (xsrfValue) {
            headers[xsrfHeaderName!] = xsrfValue
          }
        }
        
        // 请求头中添加authorization
        // https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Authorization
        if(auth) {
          headers['Authorization'] = 'Basic ' + btoa(auth.username + ':' + auth.password)
        }

        //设置请求头： 必须在open和send方法之间调用： 
        //原因send() 方法被调用后，服务端已获得头部和状态
        Object.keys(headers).forEach(name => {
          // data为空，不需要设置content-type，因为processHeaders中默认添加了content-type，此处可删除
          if(data === null && name.toLocaleLowerCase() === 'content-type') {
            delete headers[name];
          }else {
            request.setRequestHeader(name, headers[name])
          }
        })

      }

      function processCancel(): void {
         // cancel ajax: 第二种方式
        if(cancelToken) {
          cancelToken.promise.then(reason => {
            request.abort()
            reject(reason)
          })
        }
      }
      
      // 非200-300状态码处理
      function handerResponse(response: AxiosResponse):void {
        // if (response.status >= 200 && response.status < 300) {
          // 自定义合法响应状态码
          if (!validateStatus || validateStatus(response.status)) {  
          resolve(response)
        } else {
          reject(createError(
            `Request failed with status code ${response.status}`,
             config,
             null, 
             request,
             response
          ))
        }
      }
     
  })
}